package com.foodorder.service;

import com.foodorder.entity.Product;
import com.foodorder.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 库存管理服务 - 防止超卖
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class InventoryService {
    
    private final ProductRepository productRepository;
    private final StringRedisTemplate redisTemplate;
    
    // Redis分布式锁前缀
    private static final String LOCK_PREFIX = "inventory:lock:";
    private static final String STOCK_PREFIX = "inventory:stock:";
    
    // 锁超时时间（秒）
    private static final int LOCK_TIMEOUT = 10;
    
    /**
     * 预扣库存 - 使用Redis分布式锁防止超卖
     * 
     * @param productId 商品ID
     * @param quantity 预扣数量
     * @return 预扣是否成功
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public boolean reserveStock(Long productId, Integer quantity) {
        String lockKey = LOCK_PREFIX + productId;
        String lockValue = String.valueOf(System.currentTimeMillis());
        
        try {
            // 尝试获取分布式锁
            Boolean lockAcquired = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, lockValue, LOCK_TIMEOUT, TimeUnit.SECONDS);
            
            if (!Boolean.TRUE.equals(lockAcquired)) {
                log.warn("获取库存锁失败，商品ID: {}", productId);
                return false;
            }
            
            try {
                return doReserveStock(productId, quantity);
            } finally {
                // 释放锁 - 使用Lua脚本确保原子性
                releaseLock(lockKey, lockValue);
            }
            
        } catch (Exception e) {
            log.error("预扣库存失败，商品ID: {}, 数量: {}", productId, quantity, e);
            return false;
        }
    }
    
    /**
     * 实际执行库存预扣
     */
    private boolean doReserveStock(Long productId, Integer quantity) {
        // 先从Redis缓存中检查库存
        String stockKey = STOCK_PREFIX + productId;
        String cachedStock = redisTemplate.opsForValue().get(stockKey);
        
        Product product = productRepository.findById(productId).orElse(null);
        if (product == null) {
            log.warn("商品不存在，ID: {}", productId);
            return false;
        }
        
        // 如果缓存中没有库存信息，从数据库加载
        Integer currentStock;
        if (cachedStock != null) {
            currentStock = Integer.parseInt(cachedStock);
        } else {
            currentStock = product.getStock();
            // 同步到Redis缓存
            redisTemplate.opsForValue().set(stockKey, currentStock.toString(), 30, TimeUnit.MINUTES);
        }
        
        // 检查库存是否充足
        if (currentStock < quantity) {
            log.warn("库存不足，商品ID: {}, 当前库存: {}, 需要数量: {}", 
                productId, currentStock, quantity);
            return false;
        }
        
        // 更新Redis缓存中的库存
        Integer newStock = currentStock - quantity;
        redisTemplate.opsForValue().set(stockKey, newStock.toString(), 30, TimeUnit.MINUTES);
        
        // 异步更新数据库库存（使用数据库级别的原子操作）
        int updatedRows = productRepository.updateStockWithVersion(productId, quantity, product.getStock());
        
        if (updatedRows == 0) {
            // 数据库更新失败，回滚Redis缓存
            redisTemplate.opsForValue().set(stockKey, currentStock.toString(), 30, TimeUnit.MINUTES);
            log.warn("数据库库存更新失败，商品ID: {}", productId);
            return false;
        }
        
        // 更新商品状态
        if (newStock <= 0) {
            product.setStatus(Product.ProductStatus.SOLD_OUT);
            productRepository.save(product);
        }
        
        log.info("成功预扣库存，商品ID: {}, 预扣数量: {}, 剩余库存: {}", 
            productId, quantity, newStock);
        return true;
    }
    
    /**
     * 释放库存 - 用于订单取消时
     * 
     * @param productId 商品ID
     * @param quantity 释放数量
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean releaseStock(Long productId, Integer quantity) {
        String lockKey = LOCK_PREFIX + productId;
        String lockValue = String.valueOf(System.currentTimeMillis());
        
        try {
            Boolean lockAcquired = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, lockValue, LOCK_TIMEOUT, TimeUnit.SECONDS);
            
            if (!Boolean.TRUE.equals(lockAcquired)) {
                log.warn("获取库存锁失败，商品ID: {}", productId);
                return false;
            }
            
            try {
                return doReleaseStock(productId, quantity);
            } finally {
                releaseLock(lockKey, lockValue);
            }
            
        } catch (Exception e) {
            log.error("释放库存失败，商品ID: {}, 数量: {}", productId, quantity, e);
            return false;
        }
    }
    
    /**
     * 实际执行库存释放
     */
    private boolean doReleaseStock(Long productId, Integer quantity) {
        String stockKey = STOCK_PREFIX + productId;
        Product product = productRepository.findById(productId).orElse(null);
        
        if (product == null) {
            log.warn("商品不存在，ID: {}", productId);
            return false;
        }
        
        // 更新数据库库存
        int updatedRows = productRepository.increaseStock(productId, quantity);
        
        if (updatedRows > 0) {
            // 更新Redis缓存
            String cachedStock = redisTemplate.opsForValue().get(stockKey);
            if (cachedStock != null) {
                Integer newStock = Integer.parseInt(cachedStock) + quantity;
                redisTemplate.opsForValue().set(stockKey, newStock.toString(), 30, TimeUnit.MINUTES);
            }
            
            // 如果商品之前是售罄状态，现在有库存了，更新状态
            if (product.getStatus() == Product.ProductStatus.SOLD_OUT) {
                product.setStatus(Product.ProductStatus.AVAILABLE);
                productRepository.save(product);
            }
            
            log.info("成功释放库存，商品ID: {}, 释放数量: {}", productId, quantity);
            return true;
        }
        
        return false;
    }
    
    /**
     * 同步Redis缓存和数据库库存
     * 
     * @param productId 商品ID
     */
    public void syncStock(Long productId) {
        Product product = productRepository.findById(productId).orElse(null);
        if (product != null) {
            String stockKey = STOCK_PREFIX + productId;
            redisTemplate.opsForValue().set(stockKey, product.getStock().toString(), 30, TimeUnit.MINUTES);
            log.info("同步库存缓存，商品ID: {}, 库存: {}", productId, product.getStock());
        }
    }
    
    /**
     * 获取商品当前库存（优先从缓存获取）
     * 
     * @param productId 商品ID
     * @return 当前库存数量
     */
    public Integer getCurrentStock(Long productId) {
        String stockKey = STOCK_PREFIX + productId;
        String cachedStock = redisTemplate.opsForValue().get(stockKey);
        
        if (cachedStock != null) {
            return Integer.parseInt(cachedStock);
        }
        
        Product product = productRepository.findById(productId).orElse(null);
        if (product != null) {
            // 同步到缓存
            redisTemplate.opsForValue().set(stockKey, product.getStock().toString(), 30, TimeUnit.MINUTES);
            return product.getStock();
        }
        
        return 0;
    }
    
    /**
     * 批量同步库存缓存
     * 
     * @param productIds 商品ID列表
     */
    public void batchSyncStock(List<Long> productIds) {
        for (Long productId : productIds) {
            try {
                syncStock(productId);
            } catch (Exception e) {
                log.error("同步库存失败，商品ID: {}", productId, e);
            }
        }
    }
    
    /**
     * 清除库存缓存
     * 
     * @param productId 商品ID
     */
    public void clearStockCache(Long productId) {
        String stockKey = STOCK_PREFIX + productId;
        redisTemplate.delete(stockKey);
        log.info("清除库存缓存，商品ID: {}", productId);
    }
    
    /**
     * 释放分布式锁
     */
    private void releaseLock(String lockKey, String lockValue) {
        String luaScript = """
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end
            """;
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        
        try {
            redisTemplate.execute(redisScript, Collections.singletonList(lockKey), lockValue);
        } catch (Exception e) {
            log.error("释放分布式锁失败，lockKey: {}", lockKey, e);
        }
    }
    
    /**
     * 检查商品库存状态
     * 
     * @param productId 商品ID
     * @param requiredQuantity 需要的数量
     * @return 库存是否充足
     */
    public boolean checkStockAvailability(Long productId, Integer requiredQuantity) {
        Integer currentStock = getCurrentStock(productId);
        return currentStock >= requiredQuantity;
    }
} 